﻿#region Copyright(c) 1998-2013, Arnaud Colin Licence GNU GPL version 3
/* Copyright(c) 1998-2013, Arnaud Colin
 * All rights reserved.
 *
 * Licence GNU GPL version 3
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   -> Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   -> Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 */
#endregion

using System;
using System.Collections.Generic;
using System.IO;
#if NET35
using System.Linq;
#endif
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
#if NET45
using System.Threading.Tasks;
#endif

namespace XoLib.Convertion
{

    /// <summary>
    ///  Interface IChaine
    /// </summary>
    public interface IChaine
    {
        /// <summary>
        /// <para>
        /// <param name="id">int</param>
        /// <param name="c">char</param>
        /// </para>
        /// Add a charactere in the chaine
        /// <example>  </example>/// 
        /// <returns>IChaine</returns>
        /// </summary>
        IChaine Add(int id, char c);

        /// <summary>
        /// Get the original String
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        string Original
        {
            get;
        }

        /// <summary>
        /// Get the filtre
        /// <example>  </example>/// 
        /// <returns>string</returns>
        /// </summary>
        string Filtre
        {
            get;
        }
    }



    /// <summary>
    /// generic class that serves as a framework that treatment
    /// <example> RegexFilter cFileChaine  </example>/// 
    /// </summary>
    [Serializable]
    public class RegexFilter<T> where T : IChaine, new()
    {
        /// <summary>
        /// Start of the Regex Filter
        /// </summary>
        public string debut = @"^";

        /// <summary>
        /// End of the Regex Filter
        /// </summary>
        public string Fin = "$";

        /// <summary>
        /// list of chaine of the string
        /// </summary>
        public List<T> _list = new List<T>();

        /// <summary>
        /// <para>
        /// <param name="str">string</param>
        /// </para>
        /// Cut the string 
        /// <example>  Sample.Decoupe("All Bear are bleu"); </example>/// 
        /// </summary>
        public void Decoupe(string str)
        {            
            _list.Clear();

            T pChaine = new T();

            int id = 0;
            foreach (char c in str)
            {
                IChaine tt = pChaine.Add(id, c);
                if (tt != null)
                {
                    _list.Add(pChaine);
                    pChaine = (T)tt;
                }
                id++;
            }
            pChaine.Add(id, '\\');
            _list.Add(pChaine);
            pChaine = default(T);
        }

        /// <summary>
        /// No Use
        /// <example> </example>/// 
        /// <returns>bool</returns>
        /// </summary>
        public bool IsOk
        {
            get
            {
                return true;
            }
        }


        /// <summary>
        /// Get the original String
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        public string Original
        {
            get
            {
                if (_list.Count > 0)
#if NET35
                    return _list.Select(i => i.Original).Aggregate((i, j) => i + j);
#else
                    return Compatibility.NET20.Aggregate<T>(_list, "Original", null);
#endif
                else
                    return string.Empty;
            }
        }


        /// <summary>
        ///  Get the filter
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        public string Filtre
        {
            get
            {
                string s = debut;
                if (_list.Count > 0)
#if NET35
                    s += _list.Select(i => i.Filtre).Aggregate((i, j) => i + j);
#else
                    s += Compatibility.NET20.Aggregate<T>(_list, "Filtre", null);
#endif
                s += Fin;
                return s;
            }
        }


        /// <summary>
        /// <para>
        /// <param name="input">string</param>
        /// </para>
        /// Check if the input match the filter
        /// <example> Sample.IsMatch("4578R544"); </example>/// 
        /// <returns>bool</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        public bool IsMatch(string input)
        {
            return Regex.IsMatch(input, Filtre);
        }


        /// <summary>
        /// Clear all information
        /// </summary>
        public void Clear()
        {
            _list.Clear();
        }
    }


    /// <summary>
    /// Class will stock the string piece
    /// <example>  "854_AB124" => "854" Will be a CFileChaine </example>/// 
    /// </summary>
    [Serializable]
    public partial class cFileChaine : IChaine
    {
        /// <summary>
        /// Code for the script like SubString
        /// <example> SubstringCode = @"Original.SubString" </example>///
        /// </summary>
        public static string SubstringCode = @"";

        /// <summary>
        /// bracket need a specifique treatment       
        /// </summary>
        public static readonly Char[] _bracket = new Char[2] { '(', ')' };

        /// <summary>
        /// Store the original substring  
        /// </summary>
        public string _original;

        /// <summary>
        /// Store the generate filter    
        /// </summary>
        public string _filtre;

        /// <summary>
        /// Store the generate script      
        /// </summary>
        public string _script;

        /// <summary>
        /// If the substring are fixe or can change in the filter   
        /// </summary>
        public bool _IsChaineFixe = false;

        /// <summary>
        /// position of the first char of this substring  
        /// </summary>
        protected int _pos = 0;

        /// <summary>
        /// count of char of the same type
        /// </summary>
        protected int _compte = 0;

        /// <summary>
        /// Get the type of this substring like Digit or Letter
        /// <example>  </example>/// 
        /// <returns>int</returns>
        /// </summary>
        public int ActuelType
        {
            get;
            set;
        }

        /// <summary>
        /// return if the data _script is not empty or null
        /// <returns>bool</returns>
        /// </summary>
        public bool HasScript
        {
            get
            {
                return !string.IsNullOrEmpty(_script);
            }
        }

        /// <summary>
        /// <para>
        /// <param name="id">int</param>
        /// <param name="c">char</param>
        /// </para>
        /// Add Char in the chaine
        /// <example>  </example>/// 
        /// <returns>IChaine</returns>
        /// </summary>
        public IChaine Add(int id, char c)       
        {
            if (ActuelType == 0)
            {
                ActuelType = GetType(c);
                _pos = id;
                Action(ActuelType, c);
            }
            else
                if (GetType(c) == ActuelType)
                {
                    Action(ActuelType, c);
                }
                else
                {
                    switch (ActuelType)
                    {
                        case 0:
                            break;
                        case 1:
                            if (_compte > 1)
                                _filtre = "\\d{" + _compte.ToString() + "}";
                            else
                                _filtre = "\\d";

                            _script = SubstringCode + "(" + _pos.ToString() + "," + _compte.ToString() + ")";
                            break;
                        case 2:
                            if(_IsChaineFixe)
                                _script = "\"" + _script + "\"";
                            else
                                _script = SubstringCode + "(" + _pos.ToString() + "," + _compte.ToString() + ")";
                            break;
                        case 3:
                            _script = "\"" + _script + "\"";
#if !WindowsCE
                            _filtre = _filtre.Trim(Path.GetInvalidFileNameChars());
#else
                            _filtre = _filtre.Trim(Path.GetInvalidPathChars());
#endif
                            break;
                        case -1:
                            _script = string.Empty;
                            break;
                    }

                    cFileChaine pChaine = new cFileChaine();
                    pChaine.Add(id, c);
                    return pChaine;
                }

            return null;
        }

        /// <summary>
        /// Get the original String
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        public string Original
        {
            get
            {
                return _original;
            }
        }

        /// <summary>
        /// Get the Filter
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        public string Filtre
        {
            get
            {
                return _filtre;
            }
        }

        /// <summary>
        /// Get the Script
        /// <example> </example>/// 
        /// <returns>string</returns>
        /// </summary>
        public string Script
        {
            get
            {
                return _script;
            }
        }

        /// <summary>
        /// <para>
        /// <param name="typ">int</param>
        /// <param name="c">char</param>
        /// </para>
        /// Action in function of the type of char
        /// <example> </example>/// 
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        private void Action(int typ, char c)
        {
            _compte++;
            _original += c;
            switch (ActuelType)
            {
                case -1:
                    {
                        _filtre += "\\" + c;
                    }
                    break;
                case 2:
                case 3:
                    {
                       _filtre += c;
                        _script += c;
                    }
                    break;
            }
        }

        /// <summary>
        /// <para>
        /// <param name="c">char</param>
        /// </para>
        /// Get type of the char
        /// <example> GetType('1') = 1; GetType('A') = 2; </example>/// 
        /// <returns>int</returns>
        /// </summary>
#if  NET45
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
        static public int GetType(char c)
        {
            if (char.IsDigit(c))
                return 1;
            else
                if (char.IsLetter(c))
                    return 2;
                else
#if NET35
                    if (_bracket.Contains(c)) // c  (c == ')') || (c == '(')
#else              
                    if ( Compatibility.NET20.Contains(_bracket, c) )
#endif
                    {
                        return -1;
                    }
                    else

#if !WindowsCE
#if NET35
                        if (Path.GetInvalidFileNameChars().Contains(c))
#else                   
                        if (Compatibility.NET20.Contains(Path.GetInvalidFileNameChars(), c))
#endif
#else
#if NET35
                        if (Path.GetInvalidPathChars().Contains(c))
#else                   
                        if (Compatibility.NET20.Contains(Path.GetInvalidPathChars(), c))
#endif
#endif

                            return -2;
                        else
                            return 3;

        }

    }

    /// <summary>
    /// simplifies the designation and add the paramter Script
    /// </summary>
    [Serializable]
    public class RegexFileChaine : RegexFilter<cFileChaine>
    {


        /// <summary>
        /// Get the script to for the file
        /// <example>  </example>/// 
        /// <returns>string</returns>
        /// </summary>
       public string Script
       {
           get
           {
               if (_list.Count > 0)
#if NET35

                   return _list.Where(i => i.HasScript).Select(i => i.Script).Aggregate((i, j) => i + " + " + j);
#else
               {
                   return Compatibility.NET20.AggregateWhere<cFileChaine>(_list, "HasScript", "Script", '+');
               }
#endif
               else
                   return string.Empty;
           }
       }
        
    }

}
